package de.fhin.mas;

import jade.core.AID;
import jade.core.Agent;
import jade.domain.DFService;
import jade.domain.FIPAException;
import jade.domain.FIPAAgentManagement.DFAgentDescription;
import jade.domain.FIPAAgentManagement.ServiceDescription;

import java.util.HashSet;
import java.util.Set;

import de.fhin.mas.util.ArgumentCheck;

/**
 * Dieses Enum enthaelt alle Services, die an der Agentenplattform im MAS
 * registriert werden koennen.
 * <p>
 * Sollte von allen Teams verwendet werden, um sich selbst bei der
 * Agentenplattform zu registrieren oder um einen bestimmten Service zu suchen.
 * 
 * @author Alexander Weickmann
 */
public enum MASServices {

	/** Registrierter Sucher. */
	SEARCHER("Searcher"),

	/** Registrierter Manager. */
	MANAGER("Manager"),

	/** Registrierter Navigator. */
	NAVIGATOR("Navigator"),

	/** Registrierter Greifer. */
	GRIPPER("Gripper");

	/** Der Name des Services. */
	private String name;

	/**
	 * Privater Konstruktor - Keine Instanziierung von aussen erlaubt
	 * (TypesafeEnum).
	 */
	private MASServices(String name) {
		this.name = name;
	}

	/** Gibt den Namen des Services zurueck. */
	public String getName() {
		return name;
	}

	/**
	 * Durchsucht die Agentenplattform nach einem angegebenen Service.
	 * <p>
	 * Gibt <tt>null</tt> zurueck, falls kein Serviceanbieter gefunden wurde.
	 * Sollten mehrere Serviceanbieter für den gesuchten Service gefunden
	 * werden, so wird nur der erste zurueckgegeben.
	 * <p>
	 * Der anfragende Agent kommt bei der Suche ebenfalls nicht in Betracht.
	 * 
	 * @param requestorAgent
	 *            Der Agent, der die Suche anfordert.
	 * @param service
	 *            Der gesuchte Services.
	 * 
	 * @throws FIPAException
	 *             Falls ein Fehler auftritt, waehrend die Agentenplattform nach
	 *             Serviceanbietern durchsucht wird.
	 * @throws NullPointerException
	 *             Falls <tt>requestorAgent</tt> oder <tt>service</tt>
	 *             <tt>null</tt> ist.
	 */
	public static AID findService(Agent requestorAgent, MASServices service)
			throws FIPAException {

		ArgumentCheck.notNull(new Object[] { requestorAgent, service });

		ServiceDescription sd = new ServiceDescription();
		sd.setType(service.getName());

		DFAgentDescription agentDescription = new DFAgentDescription();
		agentDescription.addServices(sd);
		Set<AID> foundServices = new HashSet<AID>(4);
		for (DFAgentDescription currentFoundService : DFService.search(
				requestorAgent, agentDescription)) {
			if (currentFoundService.getName().equals(requestorAgent.getAID())) {
				continue;
			}
			foundServices.add(currentFoundService.getName());
		}

		if (foundServices.size() >= 1) {
			return foundServices.toArray(new AID[foundServices.size()])[0];
		}

		return null;
	}

	/**
	 * Durchsucht die Agentenplattform und gibt alle gefundenen Services in
	 * einem Set zurück - dies bedeutet jeder Agent ist definitiv nur einmal in
	 * der zurückgegebenen Collection enthalten, auch wenn er mehrere Services
	 * anbietet.
	 * <p>
	 * Der anfragende Agent ist in dem zurückgegebenen Set dann nicht mehr
	 * enthalten.
	 * 
	 * @param requestorAgent
	 *            Der Agent, der die Suche anfordert.
	 * 
	 * @throws FIPAException
	 *             Falls ein Fehler auftritt, waehrend die Agentenplattform nach
	 *             Serviceanbietern durchsucht wird.
	 * @throws NullPointerException
	 *             Falls <tt>requestorAgent</tt> <tt>null</tt> ist.
	 */
	public static Set<AID> findAllServices(Agent requestorAgent)
			throws FIPAException {

		ArgumentCheck.notNull(requestorAgent);

		Set<AID> allServices = new HashSet<AID>(4);
		for (DFAgentDescription currentFoundService : DFService.search(
				requestorAgent, null)) {
			if (currentFoundService.getName().equals(requestorAgent.getAID())) {
				continue;
			}
			allServices.add(currentFoundService.getName());
		}

		return allServices;
	}

	/** Gibt eine <tt>ServiceDescription</tt> dieses Services zurück. */
	public ServiceDescription getServiceDescription() {
		ServiceDescription sd = new ServiceDescription();
		sd.setType(name);
		sd.setName(name);
		return sd;
	}

}
